<template>
  <div class="city-container">
    <div class="message">
      <div>通过shader，实现飞线，扫描，扩散墙</div>
    </div>
    <div id="container" ref="container"></div>
  </div>
</template>

<script>
import { InitThree } from "../../utils/init-three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";

import { DirectionalLight, Color, Vector3, Clock, Group } from "three";

import { Radar, Wall, Fly } from "./utils/index";

export default {
  name: "effect-city",
  data() {
    return {
      initThree: null,
      scene: null,
      draco: null,
      dracoLoader: null,
      clock: new Clock(),
      isStart: false,
      startTime: { value: 0 },
      time: { value: 0 },
      effectGroup: new Group(),
    };
  },
  mounted() {
    this.initCity();
  },
  methods: {
    initCity() {
      this.initThree = new InitThree(this.$refs.container);
      this.scene = this.initThree.scene;
      this.scene.add(this.effectGroup);
      this.addLight();
      this.loadingModel();
    },

    addLight() {
      const directionalLight = new DirectionalLight(0xffffff, 2);
      directionalLight.position.set(10, 10, 10);
      this.scene.add(directionalLight);
      // const helper = new DirectionalLightHelper(directionalLight, 5);
      // this.scene.add(helper);
    },

    loadingModel() {
      const loader = new GLTFLoader();
      this.dracoLoader = new DRACOLoader();
      this.dracoLoader.setDecoderPath("model/gltf/draco/");
      loader.setDRACOLoader(this.dracoLoader);
      loader.load(
        "model/gltf/model.gltf",
        (gltf) => {
          const group = gltf.scene;
          group.traverse((child) => {
            if (child.name == "city") {
              this.setCityMaterial(child);
            }
            if (child.name == "floor") {
              child.material.color.setStyle("#333333");
            }
          });
          this.scene.add(group);
          this.isStart = true;
          this.addEffects();
          this.animate();
        },
        (xhr) => {
          console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
        },
        (error) => {
          console.log(error);
        }
      );
    },

    animate() {
      const dt = this.clock.getDelta();
      this.cityAnimate(dt);
      this.initThree.render();
      requestAnimationFrame(this.animate.bind(this));
    },

    cityAnimate(dt) {
      if (dt > 1) return false;
      this.time.value += dt;
      if (this.isStart) {
        this.startTime.value += dt * 0.6;
        if (this.startTime.value >= 1) {
          this.startTime.value = 1;
          this.isStart = false;
        }
      }
    },

    setCityMaterial(child) {
      const { material, geometry } = child;
      geometry.computeBoundingBox();
      geometry.computeBoundingSphere();
      const { max, min } = geometry.boundingBox;

      const size = new Vector3(max.x - min.x, max.y - min.y, max.z - min.z);

      material.transparent = true;
      material.color.setStyle("#367aab");
      material.onBeforeCompile = (shader) => {
        // 建筑特效
        this.addCityEffects(shader, size);
      };
    },

    addCityEffects(shader, size) {
      shader.uniforms.uStartTime = this.startTime;
      shader.uniforms.uSize = { value: size };
      shader.uniforms.uTopColor = {
        value: new Color("#fff700"),
      };

      // 修改片元着色器
      const fragment = `
                    varying vec3 vPosition;

                    uniform float uStartTime; 
                    uniform vec3 uSize;
                    uniform vec3 uTopColor;

                    void main() {
                `;
      const fragmentColor = `
                    vec3 distColor = outgoingLight;
                    float dstOpacity = diffuseColor.a;

                    float indexMix = vPosition.y / (uSize.y * 0.7);
                    distColor = mix(distColor, uTopColor, indexMix);

                    // gl_FragColor = vec4(distColor, dstOpacity * uStartTime); //透明度
                    gl_FragColor = vec4(distColor, dstOpacity);

                `;
      shader.fragmentShader = shader.fragmentShader.replace(
        "void main() {",
        fragment
      );
      shader.fragmentShader = shader.fragmentShader.replace(
        "#include <output_fragment>",
        fragmentColor
      );

      /**
       * 对顶点着色器进行修改
       */
      const vertex = `
                    varying vec3 vPosition;
                    uniform float uStartTime;
                    void main() {
                            vPosition = position;
                    `;
      const vertexPosition = `
                    vec3 transformed = vec3(position.x, position.y* uStartTime, position.z );
                `;

      shader.vertexShader = shader.vertexShader.replace(
        "void main() {",
        vertex
      );
      shader.vertexShader = shader.vertexShader.replace(
        "#include <begin_vertex>",
        vertexPosition
      );
    },

    addEffects() {
      this.addRadars();
      this.addWall();
      this.addFly();
    },

    addRadars() {
      const radarData = [
        {
          position: {
            x: 6,
            y: 0.2,
            z: 2,
          },
          radius: 2,
          border: 0.05,
          angle: Math.PI / 6,
          color: "#ff0000",
          opacity: 1,
          speed: 2,
        },
        {
          position: {
            x: -6,
            y: 0.2,
            z: -1,
          },
          border: 0.05,
          radius: 2,
          color: "#efad35",
          opacity: 0.6,
          speed: 1,
        },
      ];
      radarData.forEach((data) => {
        const mesh = Radar(data);
        mesh.material.uniforms.time = this.time;
        this.effectGroup.add(mesh);
      });
    },

    addWall() {
      const wallData = [
        {
          position: {
            x: 3,
            y: 0,
            z: 10,
          },
          speed: 1,
          color: "#24ce22",
          opacity: 0.8,
          radius: 4,
          height: 2,
          renderOrder: 5,
          diffusionMultiple: 1,
        },
      ];
      wallData.forEach((data) => {
        const mesh = Wall(data);
        mesh.material.uniforms.time = this.time;
        this.effectGroup.add(mesh);
      });
    },

    addFly() {
      const flyData = [
        {
          source: {
            x: -6,
            y: 0,
            z: 0,
          },
          target: {
            x: 6,
            y: 0,
            z: 0,
          },
          range:40,
          height: 10,
          color: "#efad35",
          speed: 1,
          size: 1,
        }
      ];
      flyData.forEach((data) => {
        const mesh = Fly(data);
        mesh.material.uniforms.time = this.time;
        mesh.renderOrder = 10;
        this.effectGroup.add(mesh);
      });
    },
  },
  beforeDestroy() {
    this.initThree.destroy();
    this.dracoLoader.dispose();
    this.dracoLoader = null;
  },
};
</script>

<style lang="scss" scoped>
.city-container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}
.message {
  height: 50px;
  width: 100%;
}
#container {
  flex: 1;
  width: 100%;
}
</style>
